package hos.http.retrofit;

import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import hos.http.HttpConfig;
import hos.http.HttpFile;
import hos.http.HttpInterceptor;
import hos.http.HttpParam;
import hos.http.HttpUtils;
import hos.http.JsonUtils;
import hos.http.anotation.Data;

/**
 * <p>Title: RetrofitParameter </p>
 * <p>Description:  </p>
 * <p>Company: www.mapuni.com </p>
 *
 * @author : 蔡俊峰
 * @version : 1.0
 * @date : 2024-04-13 12:49
 */
public class RetrofitRequest {
    private HttpInterceptor.RequestInterceptor requestInterceptor;
    private HttpInterceptor.ResponseInterceptor responseInterceptor;
    private HttpInterceptor.ExceptionInterceptor exceptionInterceptor;
    private final String url;
    private Object body;
    private Object tag;
    private final List<HttpFile> fileList = new LinkedList<>();
    private final int soapVersion;
    private final boolean soapDotNet;
    private final String soapNameSpace;
    private final String soapMethodName;
    private final String httpMethod;
    private final Type genericReturnType;
    private final Type paresType;
    private final Map<String, String> headers = new LinkedHashMap<>();
    private Map<String, String> soapHeaders;
    private final String contentType;
    private final List<HttpParam> urlParam = new LinkedList<>();
    private final List<HttpParam> bodyParam = new LinkedList<>();
    private final boolean hasBody;
    private final boolean isFormEncoded;
    private final boolean isMultipart;

    private RetrofitRequest(RetrofitRequestFactory requestFactory, Object[] args) {


        this.soapNameSpace = requestFactory.getSoapNameSpace();
        Map<String, String> headers = requestFactory.getHeaders();
        if (headers != null) {
            this.headers.putAll(headers);
        }
        List<HttpParam> factoryUrlParam = requestFactory.getUrlParam();
        if (factoryUrlParam != null) {
            urlParam.addAll(factoryUrlParam);
        }
        this.hasBody = requestFactory.isHasBody();
        this.soapVersion = requestFactory.getSoapVersion();
        this.soapMethodName = requestFactory.getSoapMethodName();
        this.httpMethod = requestFactory.getHttpMethod();
        this.genericReturnType = requestFactory.getGenericReturnType();
        this.paresType = requestFactory.getParesType();
        this.soapHeaders = requestFactory.getSoapHeaders();
        this.contentType = requestFactory.getContentType();
        this.isFormEncoded = requestFactory.isFormEncoded();
        this.isMultipart = requestFactory.isMultipart();
        this.soapDotNet = requestFactory.isSoapDotNet();
        String paramUrl = null;
        List<RetrofitParamAnnotation> methodParam = requestFactory.getMethodParam();
        for (RetrofitParamAnnotation paramAnnotation : methodParam) {
            Annotation annotation = paramAnnotation.getAnnotation();
            boolean encoded = paramAnnotation.encoded;
            Type type = paramAnnotation.getType();
            Class<?> rawType = TypeUtil.getRawType(type);
            String key = paramAnnotation.getKey();
            Object param = args[paramAnnotation.getIndex()];
            if (annotation instanceof hos.http.anotation.RequestInterceptor) {
                requestInterceptor = (HttpInterceptor.RequestInterceptor) param;
            }
            if (annotation instanceof hos.http.anotation.ResponseInterceptor) {
                responseInterceptor = (HttpInterceptor.ResponseInterceptor) param;
            }
            if (annotation instanceof hos.http.anotation.ExceptionInterceptor) {
                exceptionInterceptor = (HttpInterceptor.ExceptionInterceptor) param;
            }
            if (annotation instanceof hos.http.anotation.Url) {
                paramUrl = param.toString();
            }
            if (annotation instanceof hos.http.anotation.Tag) {
                this.tag = param;
            }
            if (annotation instanceof hos.http.anotation.SoapHeaderMap) {
                if (param != null) {
                    if (soapHeaders == null) {
                        soapHeaders = new LinkedHashMap<>();
                    }
                    if (type instanceof Map) {
                        //noinspection unchecked
                        soapHeaders.putAll((Map<String, String>) param);
                    }
                }
            }
            if (annotation instanceof hos.http.anotation.SoapHeader) {
                if (soapHeaders == null) {
                    soapHeaders = new LinkedHashMap<>();
                }
                soapHeaders.put(key, String.valueOf(param));
            }
            if (annotation instanceof hos.http.anotation.QueryMap) {
                if (param != null) {
                    //noinspection unchecked
                    urlParam.addAll(HttpParam.create((Map<String, Object>) param, encoded));
                }
            }
            if (annotation instanceof hos.http.anotation.Query) {
                if (param != null) {
                    urlParam.add(new HttpParam(key, String.valueOf(param), encoded));
                } else {
                    urlParam.add(new HttpParam(key, ""));
                }
            }
            if (annotation instanceof hos.http.anotation.ParamMap) {
                if (param != null) {
                    //noinspection unchecked
                    bodyParam.addAll(HttpParam.create((Map<String, Object>) param, encoded));
                }
            }
            if (annotation instanceof hos.http.anotation.Param) {
                if (param != null) {
                    bodyParam.add(new HttpParam(key, String.valueOf(param), encoded));
                } else {
                    bodyParam.add(new HttpParam(key, ""));
                }
            } else if (annotation instanceof hos.http.anotation.HeaderMap) {
                if (param != null) {
                    //noinspection unchecked
                    this.headers.putAll((Map<String, String>) param);
                }
            }
            if (annotation instanceof hos.http.anotation.Header) {
                if (param != null) {
                    this.headers.put(key, String.valueOf(param));
                }
            }
            if (annotation instanceof Data) {
                if (param != null) {
                    if (rawType == List.class || rawType == ArrayList.class) {
                        Type parameterUpperBound = TypeUtil.getParameterUpperBound(0, (ParameterizedType) type);
                        Class<?> rawTypeParameterUpperBound = TypeUtil.getRawType(parameterUpperBound);
                        if (rawTypeParameterUpperBound == HttpFile.class) {
                            //noinspection unchecked
                            fileList.addAll((List<HttpFile>) param);
                        } else if (rawTypeParameterUpperBound == Map.class || rawTypeParameterUpperBound == HashMap.class) {
                            Type parameterUpperBoundMap = TypeUtil.getParameterUpperBound(0, (ParameterizedType) parameterUpperBound);
                            Class<?> parameterUpperBoundMapKey = TypeUtil.getRawType(parameterUpperBoundMap);
                            if (parameterUpperBoundMapKey == String.class) {
                                //noinspection unchecked
                                body = JsonUtils.toJson((List<Map<String, ?>>) param);
                            } else {
                                body = param;
                            }
                        } else {
                            body = param;
                        }
                    } else if (rawType == HttpFile.class) {
                        fileList.add((HttpFile) param);
                    } else if (rawType == Map.class || rawType == HashMap.class) {
                        Type parameterUpperBoundMap = TypeUtil.getParameterUpperBound(0, (ParameterizedType) type);
                        Class<?> parameterUpperBoundMapKey = TypeUtil.getRawType(parameterUpperBoundMap);
                        if (parameterUpperBoundMapKey == String.class) {
                            //noinspection unchecked
                            body = JsonUtils.toJson((Map<String, ?>) param);
                        } else {
                            body = param;
                        }
                    } else {
                        body = param;
                    }
                }
            }
            if (annotation instanceof hos.http.anotation.File) {
                if (param != null) {
                    if (rawType == String.class) {
                        fileList.add(new HttpFile(key, String.valueOf(param)));
                    } else if (rawType == List.class || rawType == ArrayList.class) {
                        Type parameterUpperBound = TypeUtil.getParameterUpperBound(0, (ParameterizedType) type);
                        Class<?> rawTypeParameterUpperBound = TypeUtil.getRawType(parameterUpperBound);
                        if (rawTypeParameterUpperBound == String.class) {
                            //noinspection unchecked
                            List<String> fileListString = (List<String>) param;
                            for (String file : fileListString) {
                                fileList.add(new HttpFile(key, file));
                            }
                        } else if (rawTypeParameterUpperBound == HttpFile.class) {
                            //noinspection unchecked
                            fileList.addAll((List<HttpFile>) param);
                        }
                    }

                }
            }
        }

        // url
        String url = requestFactory.getUrl();
        if (paramUrl != null) {
            url = paramUrl;
        }
        String httpUrl = HttpConfig.config().getHost();
        if (requestFactory.isBaseUrlUse()) {
            String baseUrl = requestFactory.getBaseUrl();
            String baseUrlEnd = requestFactory.getBaseUrlEnd();
            httpUrl = HttpUtils.url(baseUrl, baseUrlEnd);
        }
        if (httpUrl == null) {
            httpUrl = url;
        } else {
            httpUrl = HttpUtils.url(httpUrl, url);
        }
        this.url = httpUrl;
    }

    public String getUrl() {
        return url;
    }

    public Object getBody() {
        return body;
    }

    public Object getTag() {
        return tag;
    }

    public List<HttpParam> getBodyParam() {
        return bodyParam;
    }

    public List<HttpFile> getFileList() {
        return fileList;
    }

    public int getSoapVersion() {
        return soapVersion;
    }

    public boolean isSoapDotNet() {
        return soapDotNet;
    }

    public String getSoapNameSpace() {
        return soapNameSpace;
    }

    public String getSoapMethodName() {
        return soapMethodName;
    }

    public String getHttpMethod() {
        return httpMethod;
    }

    public Type getGenericReturnType() {
        return genericReturnType;
    }

    public Type getParesType() {
        return paresType;
    }

    public Map<String, String> getHeaders() {
        return headers;
    }

    public Map<String, String> getSoapHeaders() {
        return soapHeaders;
    }

    public String getContentType() {
        return contentType;
    }

    public List<HttpParam> getUrlParam() {
        return urlParam;
    }

    public boolean isHasBody() {
        return hasBody;
    }

    public boolean isFormEncoded() {
        return isFormEncoded;
    }

    public boolean isMultipart() {
        return isMultipart;
    }

    public HttpInterceptor.RequestInterceptor getRequestInterceptor() {
        return requestInterceptor;
    }

    public HttpInterceptor.ResponseInterceptor getResponseInterceptor() {
        return responseInterceptor;
    }

    public HttpInterceptor.ExceptionInterceptor getExceptionInterceptor() {
        return exceptionInterceptor;
    }

    public static RetrofitRequest parseAnnotations(RetrofitRequestFactory requestFactory, Object[] args) {
        return new RetrofitRequest(requestFactory, args);
    }

    @Override
    public String toString() {
        return "RetrofitRequest{" +
                "requestInterceptor=" + requestInterceptor +
                ", responseInterceptor=" + responseInterceptor +
                ", exceptionInterceptor=" + exceptionInterceptor +
                ", url='" + url + '\'' +
                ", body=" + body +
                ", tag=" + tag +
                ", fileList=" + fileList +
                ", soapVersion=" + soapVersion +
                ", soapDotNet=" + soapDotNet +
                ", soapNameSpace='" + soapNameSpace + '\'' +
                ", httpMethod='" + httpMethod + '\'' +
                ", genericReturnType=" + genericReturnType +
                ", headers=" + headers +
                ", soapHeaders=" + soapHeaders +
                ", contentType='" + contentType + '\'' +
                ", urlParam=" + urlParam +
                ", bodyParam=" + bodyParam +
                ", hasBody=" + hasBody +
                ", isFormEncoded=" + isFormEncoded +
                ", isMultipart=" + isMultipart +
                '}';
    }
}
